﻿using System;
using System.Collections.Generic;
using System.Linq;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Core.Data;
using Urs.Data.Domain.Users;
using Urs.Data.Domain.Logging;
using Urs.Data;
using Urs.Data.Domain.Configuration;

namespace Urs.Services.Logging
{
    public class ActivityLogService : IActivityLogService
    {
        #region Constants
        private const string ACTIVITYTYPE_ALL_KEY = "Urs.activitytype.all";
        private const string ACTIVITYTYPE_BY_ID_KEY = "Urs.activitytype.id-{0}";
        private const string ACTIVITYTYPE_PATTERN_KEY = "Urs.activitytype.";
        #endregion

        #region Fields

        private readonly ICacheManager _cacheManager;
        private readonly IRepository<ActivityLog> _activityLogRepository;
        private readonly IRepository<ActivityLogType> _activityLogTypeRepository;
        private readonly IWorkContext _workContext;
        private readonly IDbContext _dbContext;
        private readonly IDataProvider _dataProvider;
        private readonly CommonSettings _commonSettings;
        #endregion
        
        #region Ctor
        public ActivityLogService(ICacheManager cacheManager,
            IRepository<ActivityLog> activityLogRepository,
            IRepository<ActivityLogType> activityLogTypeRepository,
            IWorkContext workContext,
            IDbContext dbContext, IDataProvider dataProvider, CommonSettings commonSettings)
        {
            this._cacheManager = cacheManager;
            this._activityLogRepository = activityLogRepository;
            this._activityLogTypeRepository = activityLogTypeRepository;
            this._workContext = workContext;
            this._dbContext = dbContext;
            this._dataProvider = dataProvider;
            this._commonSettings = commonSettings;
        }

        #endregion

        #region Methods

        public virtual void InsertActivityType(ActivityLogType activityLogType)
        {
            if (activityLogType == null)
                throw new ArgumentNullException("activityLogType");

            _activityLogTypeRepository.Insert(activityLogType);
            _cacheManager.RemoveByPattern(ACTIVITYTYPE_PATTERN_KEY);
        }

        public virtual void UpdateActivityType(ActivityLogType activityLogType)
        {
            if (activityLogType == null)
                throw new ArgumentNullException("activityLogType");

            _activityLogTypeRepository.Update(activityLogType);
            _cacheManager.RemoveByPattern(ACTIVITYTYPE_PATTERN_KEY);
        }
                
        public virtual void DeleteActivityType(ActivityLogType activityLogType)
        {
            if (activityLogType == null)
                throw new ArgumentNullException("activityLogType");

            _activityLogTypeRepository.Delete(activityLogType);
            _cacheManager.RemoveByPattern(ACTIVITYTYPE_PATTERN_KEY);
        }
        
        public virtual IList<ActivityLogType> GetAllActivityTypes()
        {
            string key = string.Format(ACTIVITYTYPE_ALL_KEY);
            return _cacheManager.Get(key, () =>
            {
                var query = from alt in _activityLogTypeRepository.Table
                            orderby alt.Name
                            select alt;
                var activityLogTypes = query.ToList();
                return activityLogTypes;
            });
        }
        
        public virtual ActivityLogType GetActivityTypeById(int activityLogTypeId)
        {
            if (activityLogTypeId == 0)
                return null;

            string key = string.Format(ACTIVITYTYPE_BY_ID_KEY, activityLogTypeId);
            return _cacheManager.Get(key, () =>
            {
                return _activityLogTypeRepository.GetById(activityLogTypeId);
            });
        }

        public virtual ActivityLog InsertActivity(string systemKeyword,
            string comment, params object[] commentParams)
        {
            return InsertActivity(systemKeyword, comment, _workContext.CurrentUser, commentParams);
        }
        

        public virtual ActivityLog InsertActivity(string systemKeyword, 
            string comment, User user, params object[] commentParams)
        {
            if (user == null)
                return null;

            var activityTypes = GetAllActivityTypes();
            var activityType = activityTypes.ToList().Find(at => at.SystemKeyword == systemKeyword);
            if (activityType == null || !activityType.Enabled)
                return null;

            comment = CommonHelper.EnsureNotNull(comment);
            comment = string.Format(comment, commentParams);
            comment = CommonHelper.EnsureMaximumLength(comment, 4000);

            

            var activity = new ActivityLog();
            activity.ActivityLogTypeId = activityType.Id;
            activity.UserId = user.Id;
            activity.Comment = comment;
            activity.CreateTime = DateTime.Now;

            _activityLogRepository.Insert(activity);

            return activity;
        }
        
        public virtual void DeleteActivity(ActivityLog activityLog)
        {
            if (activityLog == null)
                throw new ArgumentNullException("activityLog");

            _activityLogRepository.Delete(activityLog);
        }

        public virtual IPagedList<ActivityLog> GetAllActivities(DateTime? createdOnFrom,
            DateTime? createdOnTo, int? userId, int activityLogTypeId,
            int pageIndex, int pageSize)
        {
            var query = _activityLogRepository.Table;
            if (createdOnFrom.HasValue)
                query = query.Where(al => createdOnFrom.Value <= al.CreateTime);
            if (createdOnTo.HasValue)
                query = query.Where(al => createdOnTo.Value >= al.CreateTime);
            if (activityLogTypeId > 0)
                query = query.Where(al => activityLogTypeId == al.ActivityLogTypeId);
            if (userId.HasValue)
                query = query.Where(al => userId.Value == al.UserId);

            query = query.OrderByDescending(al => al.CreateTime);

            var activityLog = new PagedList<ActivityLog>(query, pageIndex, pageSize);
            return activityLog;
        }
        
        public virtual ActivityLog GetActivityById(int activityLogId)
        {
            if (activityLogId == 0)
                return null;

            
            var query = from al in _activityLogRepository.Table
                        where al.Id == activityLogId
                        select al;
            var activityLog = query.SingleOrDefault();
            return activityLog;
        }

        public virtual void ClearAllActivities()
        {
            if (_commonSettings.UseStoredProceduresIfSupported)
            {
                _dbContext.ExecuteSqlCommand("DELETE FROM ActivityLog");
            }
            else
            {
                var activityLog = _activityLogRepository.Table.ToList();
                foreach (var activityLogItem in activityLog)
                    _activityLogRepository.Delete(activityLogItem);
            }
        }
        #endregion

    }
}
